/* ChatController by Andrew Loewenstern (andrew@cubetech.com).*/
 /*  This is a really simple */
 /* class that clearly shows you how to implement Distributed Objects */
 /* in a NeXTSTEP application.  This example is totally free, and */
 /* there is no warranty.  If you have questions about it, send me */
 /* some mail at andrew@cubetech.com. */

#import "ChatController.h"
#import <remote/NXProxy.h>

@implementation ChatController

- getHostName			/* gets the host of the server app */
{
  [NXApp runModalFor:[hostName window]];
  [[hostName window] close];
  return self;
}

- appDidInit:sender
{
  NXConnection *conn;
  
  [self getHostName];		/* get the host of the server */
  
				/* try to connect to server */
  server = [NXConnection connectToName:"ChatServer" onHost:[hostName
							    stringValue]];
  if(!server)			/* we must be the server */
    {
				/* allocate the client list */
      clients = [[List allocFromZone:[self zone]] init];
				/* register ourselves */
      conn = [NXConnection registerRoot:self withName:"ChatServer"];
      [[conversation window] setTitle:"ChatServer"];
      server = self;		/* messages we send to the server will */
				/* now go to us... */
      weAreServer = YES;
    }
  else				/* we must be the client */
    {
				/* Get the connection for the server */
      conn = [server connectionForProxy];
				/* tell the runtime system that the */
				/* server responds to the Chat */
				/* protocol */
      [server setProtocolForProxy:@protocol(Chat)];
      
      weAreServer = NO;
      [server checkIn:self];	/* check in with the server */
    }
				/* we want to know if either the */
				/* server or the clients die... */
  [conn registerForInvalidationNotification:self];
  [conn runFromAppKit];		/* handle messages in the main event */
				/* loop */
  [entry selectText:self];
  return self;
}

- checkIn:
  sender
{
  if(weAreServer)
    {
				/* stuff the client into the list so */
				/* we can send messages to it...*/
      [clients addObjectIfAbsent:sender];
				/* and tell the runtime system that */
				/* the client implements the Chat */
				/* protocol */
      [sender setProtocolForProxy:@protocol(Chat)];
    }
  return self;
}

- checkOut:
  sender
{
  if(weAreServer)
    [clients removeObject:sender]; /* remove the client so we don't */
				   /* send messages to it anymore */
  return self;
}


				/* acceptMessage:from: is sent by the */
				/* server to every client.  It returns */
				/* void so the server does not wait */
				/* for each client to accept the */
				/* message and return...*/

- (void) acceptMessage:(in char *)message from:(in char *)name
{
  int i;
  char *comp = NXZoneMalloc([self zone], strlen(name) +
			    strlen(message) + 4);

				/* create the string to put in the */
				/* conversation window */
  sprintf(comp, "%s: %s\n", name, message);
  
				/* then select the very last line, */
				/* replace the selection with the */
				/* string, select the last line again, */
				/* and scroll it till it's visible */
  [conversation setSel:[conversation textLength] :[conversation
						   textLength]];
  [conversation replaceSel:(const char *)comp];
  [conversation setSel:[conversation textLength] :[conversation
						   textLength]];
  [conversation scrollSelToVisible];
  
  NXZoneFree([self zone], comp); /* free the string */


				/* for each client, we send the same */
				/* acceptMesage:from: message.  Each */
				/* client will recieve it and display */
				/* the mesage */
  if(weAreServer)
    {
      for(i = 0; i < [clients count]; i++)
	[[clients objectAt:i] acceptMessage:message from:name];
    }
}

      
- senderIsInvalid:
  sender			/* we will recieve this message if the */
				/* server dies, or if a client dies */
				/* (if we are the server */
{
  if(weAreServer)		/* then remove the client */
    [self checkOut:sender];
  else				/* if there's no server, then we die */
    [NXApp terminate:self];
  
  return self;
}

- send:sender
{
				/* send the message to the server! */
				/* It's as simple as this! */
  [server acceptMessage:[entry stringValue] from:getlogin()];
  [entry selectText:self];
  return self;
}

- appWillTerminate:
  sender
{
				/* if we are a client, we should tell */
				/* the server not to send us messages. */
				/* Although the server will get a */
				/* senderIsInvalid message, it's */
				/* better to make sure the server */
				/* doesn't try to contact us after we */
				/* no longer exist... */
  if(!weAreServer)
    [server checkOut:self];
  return self;
}
@end
